<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>🌟 Vue 3 script setup + 类型推断：TS 丝滑连招</title>
  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
  <style>
    body {
      font-family: 'Arial', sans-serif;
      max-width: 1000px;
      margin: 0 auto;
      padding: 20px;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      min-height: 100vh;
    }
    .container {
      background: white;
      border-radius: 15px;
      padding: 30px;
      margin: 20px 0;
      box-shadow: 0 10px 30px rgba(0,0,0,0.2);
    }
    .title {
      color: #42b983;
      text-align: center;
      font-size: 32px;
      margin-bottom: 30px;
      text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
    }
    .section {
      background: #f8f9fa;
      border-radius: 10px;
      padding: 20px;
      margin: 20px 0;
      border-left: 5px solid #42b983;
    }
    .section h3 {
      color: #333;
      margin-top: 0;
      display: flex;
      align-items: center;
      gap: 10px;
    }
    .code-block {
      background: #2d3748;
      color: #e2e8f0;
      padding: 15px;
      border-radius: 8px;
      font-family: 'Courier New', monospace;
      margin: 10px 0;
      overflow-x: auto;
      border-left: 4px solid #42b983;
    }
    .feature-card {
      background: #fff;
      border: 2px solid #42b983;
      border-radius: 10px;
      padding: 20px;
      margin: 15px 0;
      box-shadow: 0 4px 8px rgba(0,0,0,0.1);
    }
    .demo-area {
      background: #e8f5e8;
      border-radius: 10px;
      padding: 20px;
      margin: 15px 0;
      border: 2px dashed #42b983;
    }
    .input-group {
      display: flex;
      align-items: center;
      gap: 15px;
      margin: 10px 0;
      flex-wrap: wrap;
    }
    .input-group label {
      font-weight: bold;
      min-width: 100px;
      color: #2d3748;
    }
    .input-group input, .input-group select {
      padding: 8px 12px;
      border: 2px solid #e2e8f0;
      border-radius: 6px;
      font-size: 14px;
      transition: border-color 0.3s;
    }
    .input-group input:focus, .input-group select:focus {
      outline: none;
      border-color: #42b983;
    }
    .type-badge {
      background: #42b983;
      color: white;
      padding: 4px 8px;
      border-radius: 4px;
      font-size: 12px;
      font-weight: bold;
    }
    .error-badge {
      background: #e74c3c;
      color: white;
      padding: 4px 8px;
      border-radius: 4px;
      font-size: 12px;
      font-weight: bold;
    }
    .success-badge {
      background: #27ae60;
      color: white;
      padding: 4px 8px;
      border-radius: 4px;
      font-size: 12px;
      font-weight: bold;
    }
    .highlight {
      background: #fff3cd;
      padding: 10px;
      border-radius: 6px;
      border-left: 4px solid #f39c12;
      margin: 10px 0;
    }
    .tip {
      background: #d1ecf1;
      padding: 15px;
      border-radius: 8px;
      border-left: 4px solid #17a2b8;
      margin: 15px 0;
    }
    .comparison {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 20px;
      margin: 20px 0;
    }
    .comparison-item {
      background: #fff;
      padding: 20px;
      border-radius: 10px;
      border: 2px solid #e9ecef;
    }
    .comparison-item h4 {
      margin-top: 0;
      color: #495057;
    }
    button {
      background: #42b983;
      color: white;
      border: none;
      padding: 10px 20px;
      border-radius: 6px;
      cursor: pointer;
      font-size: 14px;
      transition: background 0.3s;
      margin: 5px;
    }
    button:hover {
      background: #3aa876;
    }
    .btn-danger {
      background: #e74c3c;
    }
    .btn-danger:hover {
      background: #c0392b;
    }
    .btn-warning {
      background: #f39c12;
    }
    .btn-warning:hover {
      background: #e67e22;
    }
    .output {
      background: #2d3748;
      color: #e2e8f0;
      padding: 15px;
      border-radius: 8px;
      font-family: 'Courier New', monospace;
      margin: 10px 0;
      min-height: 50px;
      border-left: 4px solid #42b983;
    }
    .grid-3 {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
      gap: 20px;
      margin: 20px 0;
    }
    .card {
      background: white;
      border-radius: 10px;
      padding: 20px;
      box-shadow: 0 4px 8px rgba(0,0,0,0.1);
      border-left: 4px solid #42b983;
    }
  </style>
</head>
<body>
  <div id="app">
    <div class="container">
      <h1 class="title">🌟 Vue 3 script setup + 类型推断：TS 丝滑连招</h1>
      
      <div class="section">
        <h3>🎯 学习目标：掌握 script setup 语法和 TypeScript 类型推断</h3>
        <p>script setup 语法让代码更简洁，配合 TypeScript 泛型让类型更安全！</p>
      </div>

      <!-- 技巧清单 -->
      <div class="feature-card">
        <h3>🥋 TS 丝滑连招技巧清单</h3>
        <div class="highlight">
          <strong>✨ 技巧1：</strong>顶层变量自动暴露给模板，无需 return
        </div>
        <div class="highlight">
          <strong>✨ 技巧2：</strong>用 interface / type 描述 props & emit，让 Vetur/Volar 给你智能提示
        </div>
        <div class="highlight">
          <strong>✨ 技巧3：</strong>给 ref/reactive 加泛型，写错会飘红，比 console 报错更早
        </div>
      </div>

      <!-- 泛型 ref 示例 -->
      <div class="section">
        <h3>🎯 示例1：泛型 ref - 类型安全的响应式数据</h3>
        <div class="demo-area">
          <div class="input-group">
            <label>用户名 (string):</label>
            <input v-model="username" placeholder="输入用户名">
            <span class="type-badge">类型: {{ typeof username }}</span>
          </div>
          
          <div class="input-group">
            <label>年龄 (number):</label>
            <input v-model.number="age" type="number" placeholder="输入年龄">
            <span class="type-badge">类型: {{ typeof age }}</span>
          </div>
          
          <div class="input-group">
            <label>是否激活 (boolean):</label>
            <select v-model="isActive">
              <option :value="true">是</option>
              <option :value="false">否</option>
            </select>
            <span class="type-badge">类型: {{ typeof isActive }}</span>
          </div>
          
          <div class="output">
            用户信息：{{ userInfo }}
          </div>
        </div>
        
        <div class="code-block">
// 泛型 ref - 类型安全的响应式数据
const username = ref&lt;string&gt;('张三')  // 必须是 string 类型
const age = ref&lt;number&gt;(25)           // 必须是 number 类型
const isActive = ref&lt;boolean&gt;(true)  // 必须是 boolean 类型

// 计算属性 - 自动类型推断
const userInfo = computed(() => {
  return `${username.value} (${age.value}岁) - ${isActive.value ? '已激活' : '未激活'}`
})
        </div>
      </div>

      <!-- 泛型 reactive 示例 -->
      <div class="section">
        <h3>📦 示例2：泛型 reactive - 复杂对象类型推断</h3>
        <div class="demo-area">
          <div class="grid-3">
            <div class="card">
              <h4>产品信息</h4>
              <div class="input-group">
                <label>名称:</label>
                <input v-model="product.name" placeholder="产品名称">
              </div>
              <div class="input-group">
                <label>价格:</label>
                <input v-model.number="product.price" type="number" placeholder="价格">
              </div>
              <div class="input-group">
                <label>库存:</label>
                <input v-model.number="product.stock" type="number" placeholder="库存">
              </div>
            </div>
            
            <div class="card">
              <h4>用户信息</h4>
              <div class="input-group">
                <label>姓名:</label>
                <input v-model="userProfile.name" placeholder="姓名">
              </div>
              <div class="input-group">
                <label>邮箱:</label>
                <input v-model="userProfile.email" type="email" placeholder="邮箱">
              </div>
              <div class="input-group">
                <label>等级:</label>
                <select v-model="userProfile.level">
                  <option value="bronze">青铜</option>
                  <option value="silver">白银</option>
                  <option value="gold">黄金</option>
                  <option value="platinum">铂金</option>
                </select>
              </div>
            </div>
            
            <div class="card">
              <h4>订单统计</h4>
              <div class="output">
                <div>订单状态: {{ order.status }}</div>
                <div>总数量: {{ order.totalQuantity }}</div>
                <div>总金额: ¥{{ order.totalAmount.toFixed(2) }}</div>
                <div>创建时间: {{ order.createdAt.toLocaleString() }}</div>
              </div>
            </div>
          </div>
        </div>
        
        <div class="code-block">
// 定义 TypeScript 接口
interface Product {
  id: number
  name: string
  price: number
  stock: number
  category?: string
}

interface UserProfile {
  id: number
  name: string
  email: string
  level: 'bronze' | 'silver' | 'gold' | 'platinum'
  registerDate: Date
}

interface Order {
  id: number
  userId: number
  items: Array&lt;{ productId: number; quantity: number; price: number }&gt;
  status: 'pending' | 'processing' | 'completed' | 'cancelled'
  totalQuantity: number
  totalAmount: number
  createdAt: Date
}

// 泛型 reactive - 类型安全的复杂对象
const product = reactive&lt;Product&gt;({
  id: 1,
  name: 'iPhone 15',
  price: 5999,
  stock: 100,
  category: '电子产品'
})

const userProfile = reactive&lt;UserProfile&gt;({
  id: 1,
  name: '李四',
  email: 'lisi@example.com',
  level: 'gold',
  registerDate: new Date('2023-01-01')
})

const order = reactive&lt;Order&gt;({
  id: 1001,
  userId: 1,
  items: [],
  status: 'pending',
  totalQuantity: 0,
  totalAmount: 0,
  createdAt: new Date()
})
        </div>
      </div>

      <!-- Props 和 Emit 类型定义 -->
      <div class="section">
        <h3>🎪 示例3：Props 和 Emit 的类型定义</h3>
        <div class="demo-area">
          <div class="input-group">
            <label>组件标题:</label>
            <input v-model="componentTitle" placeholder="输入组件标题">
          </div>
          <div class="input-group">
            <label>消息内容:</label>
            <textarea v-model="messageContent" placeholder="输入消息内容" rows="3" style="flex: 1;"></textarea>
          </div>
          <div class="input-group">
            <label>消息类型:</label>
            <select v-model="messageType">
              <option value="info">信息</option>
              <option value="success">成功</option>
              <option value="warning">警告</option>
              <option value="error">错误</option>
            </select>
          </div>
          <div class="input-group">
            <button @click="sendMessage">发送消息</button>
            <button @click="clearMessages" class="btn-danger">清空消息</button>
            <button @click="toggleVisible" class="btn-warning">{{ visible ? '隐藏' : '显示' }}</button>
          </div>
          <div class="output">
            <div>组件状态: {{ componentStatus }}</div>
            <div>消息数量: {{ messages.length }}</div>
            <div>可见状态: {{ visible }}</div>
          </div>
        </div>
        
        <div class="code-block">
// Props 接口定义
interface MessageProps {
  title: string
  content: string
  type: 'info' | 'success' | 'warning' | 'error'
  duration?: number
  closable?: boolean
}

// Emit 事件定义
interface MessageEmits {
  (e: 'send', message: MessageProps): void
  (e: 'close', id: number): void
  (e: 'clear'): void
}

// 组件内部状态
const componentTitle = ref&lt;string&gt;('消息组件')
const messageContent = ref&lt;string&gt;('这是一条测试消息')
const messageType = ref&lt;MessageProps['type']&gt;('info')
const visible = ref&lt;boolean&gt;(true)
const messages = ref&lt;Array&lt;MessageProps &amp; { id: number }&gt;&gt;([])
const componentStatus = ref&lt;string&gt;('就绪')

// 方法定义
const sendMessage = () => {
  const newMessage: MessageProps &amp; { id: number } = {
    id: Date.now(),
    title: componentTitle.value,
    content: messageContent.value,
    type: messageType.value,
    duration: 3000,
    closable: true
  }
  messages.value.push(newMessage)
  componentStatus.value = '消息已发送'
  
  setTimeout(() => {
    componentStatus.value = '就绪'
  }, 1000)
}

const clearMessages = () => {
  messages.value = []
  componentStatus.value = '消息已清空'
  
  setTimeout(() => {
    componentStatus.value = '就绪'
  }, 1000)
}

const toggleVisible = () => {
  visible.value = !visible.value
}
        </div>
      </div>

      <!-- 类型推断对比 -->
      <div class="section">
        <h3>⚖️ 类型推断对比：有类型 vs 无类型</h3>
        <div class="comparison">
          <div class="comparison-item">
            <h4>❌ 无类型定义（容易出错）</h4>
            <div class="code-block" style="background: #fff5f5; border-color: #e74c3c; color: #333;">
const data = ref({})  // 可以是任何类型
const count = ref()     // 类型不明确
const user = reactive({
  name: '',           // 没有类型约束
  age: 0              // 可以是任意值
})

// 容易写出错误代码
user.age = 'invalid'  // 不会报错，但逻辑错误
count.value = 'not a number'  // 运行时可能出错
            </div>
            <div class="error-badge">容易出错</div>
          </div>
          
          <div class="comparison-item">
            <h4>✅ 有类型定义（类型安全）</h4>
            <div class="code-block">
interface UserData {
  name: string
  age: number
  email?: string
}

const data = ref&lt;UserData | null&gt;(null)
const count = ref&lt;number&gt;(0)
const user = reactive&lt;UserData&gt;({
  name: '张三',
  age: 25
})

// 类型检查会报错
user.age = 'invalid'      // ❌ 类型错误
count.value = 'not number' // ❌ 类型错误
            </div>
            <div class="success-badge">类型安全</div>
          </div>
        </div>
      </div>

      <!-- 最佳实践总结 -->
      <div class="section">
        <h3>🏆 script setup + TypeScript 最佳实践</h3>
        <div class="tip">
          <strong>💡 最佳实践1：</strong>始终为复杂对象定义接口，提高代码可维护性
        </div>
        <div class="tip">
          <strong>💡 最佳实践2：</strong>使用泛型 ref/reactive，让类型错误在编译时发现
        </div>
        <div class="tip">
          <strong>💡 最佳实践3：</strong>为 props 和 emit 定义类型，获得更好的 IDE 支持
        </div>
        <div class="tip">
          <strong>💡 最佳实践4：</strong>使用联合类型限制可选值，避免魔法字符串
        </div>
        <div class="tip">
          <strong>💡 最佳实践5：</strong>利用类型推断，避免重复的类型注解
        </div>
      </div>

      <!-- 学习要点 -->
      <div class="section">
        <h3>📚 学习要点总结</h3>
        <div class="grid-3">
          <div class="card">
            <h4>🎯 script setup 优势</h4>
            <ul>
              <li>更少的样板代码</li>
              <li>更好的运行时性能</li>
              <li>更好的 IDE 类型推断</li>
              <li>顶层绑定自动暴露</li>
            </ul>
          </div>
          
          <div class="card">
            <h4>🔒 类型安全</h4>
            <ul>
              <li>编译时错误检查</li>
              <li>更好的代码提示</li>
              <li>减少运行时错误</li>
              <li>提高代码可维护性</li>
            </ul>
          </div>
          
          <div class="card">
            <h4>⚡ 开发效率</h4>
            <ul>
              <li>智能代码补全</li>
              <li>自动类型推断</li>
              <li>重构更安全</li>
              <li>文档即代码</li>
            </ul>
          </div>
        </div>
      </div>
    </div>
  </div>

  <script>
    // 定义 TypeScript 接口（在纯JS环境中模拟类型检查）
    const { createApp, ref, reactive, computed } = Vue;

    createApp({
      setup() {
        // 示例1：泛型 ref - 类型安全的响应式数据
        const username = ref('张三');  // 模拟 string 类型
        const age = ref(25);           // 模拟 number 类型
        const isActive = ref(true);    // 模拟 boolean 类型

        // 计算属性 - 自动类型推断
        const userInfo = computed(() => {
          return `${username.value} (${age.value}岁) - ${isActive.value ? '已激活' : '未激活'}`;
        });

        // 示例2：泛型 reactive - 复杂对象类型推断
        // 模拟接口定义
        const product = reactive({
          id: 1,
          name: 'iPhone 15',
          price: 5999,
          stock: 100,
          category: '电子产品'
        });

        const userProfile = reactive({
          id: 1,
          name: '李四',
          email: 'lisi@example.com',
          level: 'gold',
          registerDate: new Date('2023-01-01')
        });

        const order = reactive({
          id: 1001,
          userId: 1,
          items: [],
          status: 'pending',
          totalQuantity: 0,
          totalAmount: 0,
          createdAt: new Date()
        });

        // 示例3：Props 和 Emit 的类型定义
        const componentTitle = ref('消息组件');
        const messageContent = ref('这是一条测试消息');
        const messageType = ref('info');
        const visible = ref(true);
        const messages = ref([]);
        const componentStatus = ref('就绪');

        // 方法定义
        const sendMessage = () => {
          const newMessage = {
            id: Date.now(),
            title: componentTitle.value,
            content: messageContent.value,
            type: messageType.value,
            duration: 3000,
            closable: true
          };
          messages.value.push(newMessage);
          componentStatus.value = '消息已发送';
          
          setTimeout(() => {
            componentStatus.value = '就绪';
          }, 1000);
        };

        const clearMessages = () => {
          messages.value = [];
          componentStatus.value = '消息已清空';
          
          setTimeout(() => {
            componentStatus.value = '就绪';
          }, 1000);
        };

        const toggleVisible = () => {
          visible.value = !visible.value;
        };

        // 返回所有需要在模板中使用的数据和方法
        return {
          // 示例1数据
          username,
          age,
          isActive,
          userInfo,
          
          // 示例2数据
          product,
          userProfile,
          order,
          
          // 示例3数据
          componentTitle,
          messageContent,
          messageType,
          visible,
          messages,
          componentStatus,
          
          // 方法
          sendMessage,
          clearMessages,
          toggleVisible
        };
      }
    }).mount('#app');
  </script>
</body>
</html>